/*
 * 
 * 
 * 
 */
package com.hboxs.asl.util;

import com.hboxs.asl.Setting;

import freemarker.template.TemplateException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.im4java.core.*;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.*;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;

import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

/**
 * Utils - 图片处理(支持JDK、GraphicsMagick、ImageMagick)
 */
public final class ImageUtils {

    /**
     * 头像压缩尺寸
     */
    private static final int IMAGEWIDTH = 150;

    /**
     * 处理类型
     */
    private enum Type {

        /**
         * 自动
         */
        auto,

        /**
         * jdk
         */
        jdk,

        /**
         * GraphicsMagick
         */
        graphicsMagick,

        /**
         * ImageMagick
         */
        imageMagick
    }

    /**
     * 处理类型
     */
    private static Type type = Type.auto;

    /**
     * GraphicsMagick程序路径
     */
    private static String graphicsMagickPath;

    /**
     * ImageMagick程序路径
     */
    private static String imageMagickPath;

    /**
     * 背景颜色
     */
    private static final Color BACKGROUND_COLOR = Color.white;

    /**
     * 目标图片品质(取值范围: 0 - 100)
     */
    private static final int DEST_QUALITY = 88;

    static {
        if (graphicsMagickPath == null) {
            String osName = System.getProperty("os.name").toLowerCase();
            if (osName.indexOf("windows") >= 0) {
                String pathVariable = System.getenv("Path");
                if (pathVariable != null) {
                    String[] paths = pathVariable.split(";");
                    for (String path : paths) {
                        File gmFile = new File(path.trim() + "/gm.exe");
                        File gmdisplayFile = new File(path.trim() + "/gmdisplay.exe");
                        if (gmFile.exists() && gmdisplayFile.exists()) {
                            graphicsMagickPath = path.trim();
                            break;
                        }
                    }
                }
            }
        }

        if (imageMagickPath == null) {
            String osName = System.getProperty("os.name").toLowerCase();
            if (osName.indexOf("windows") >= 0) {
                String pathVariable = System.getenv("Path");
                if (pathVariable != null) {
                    String[] paths = pathVariable.split(";");
                    for (String path : paths) {
                        File convertFile = new File(path.trim() + "/convert.exe");
                        File compositeFile = new File(path.trim() + "/composite.exe");
                        if (convertFile.exists() && compositeFile.exists()) {
                            imageMagickPath = path.trim();
                            break;
                        }
                    }
                }
            }
        }

        if (type == Type.auto) {
            try {
                IMOperation operation = new IMOperation();
                operation.version();
                IdentifyCmd identifyCmd = new IdentifyCmd(true);
                if (graphicsMagickPath != null) {
                    identifyCmd.setSearchPath(graphicsMagickPath);
                }
                identifyCmd.run(operation);
                type = Type.graphicsMagick;
            } catch (Throwable e1) {
                try {
                    IMOperation operation = new IMOperation();
                    operation.version();
                    IdentifyCmd identifyCmd = new IdentifyCmd(false);
                    identifyCmd.run(operation);
                    if (imageMagickPath != null) {
                        identifyCmd.setSearchPath(imageMagickPath);
                    }
                    type = Type.imageMagick;
                } catch (Throwable e2) {
                    type = Type.jdk;
                }
            }
        }
    }

    /**
     * 不可实例化
     */
    private ImageUtils() {
    }

    /**
     * 等比例图片缩放
     *
     * @param srcImage   源文件
     * @param destWidth  目标宽度
     * @param destHeight 目标高度
     */
    public static BufferedImage zoom(BufferedImage srcImage, int destWidth, int destHeight) {
        Assert.notNull(srcImage);
        Assert.state(destWidth > 0);
        Assert.state(destHeight > 0);
        Graphics2D graphics2D = null;
        BufferedImage destImage = null;
        try {
            int srcWidth = srcImage.getWidth();
            int srcHeight = srcImage.getHeight();
            int width = destWidth;
            int height = destHeight;
            if (srcHeight >= srcWidth) {
                width = (int) Math.round(((destHeight * 1.0 / srcHeight) * srcWidth));
            } else {
                height = (int) Math.round(((destWidth * 1.0 / srcWidth) * srcHeight));
            }
            destImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
            graphics2D = destImage.createGraphics();
            graphics2D.setBackground(BACKGROUND_COLOR);
            graphics2D.clearRect(0, 0, destWidth, destHeight);
            graphics2D.drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), (destWidth / 2) - (width / 2), (destHeight / 2) - (height / 2), null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (graphics2D != null) {
                graphics2D.dispose();
            }
        }

        return destImage;
    }
    
    /**
     * 等比例图片缩放
     *
     * @param srcFile    源文件
     * @param destFile   目标文件
     * @param destWidth  目标宽度
     * @param destHeight 目标高度
     */
    public static void zoom(File srcFile, File destFile, int destWidth, int destHeight) {
        Assert.notNull(srcFile);
        Assert.notNull(destFile);
        Assert.state(destWidth > 0);
        Assert.state(destHeight > 0);
        if (type == Type.jdk) {
            Graphics2D graphics2D = null;
            ImageOutputStream imageOutputStream = null;
            ImageWriter imageWriter = null;
            try {
                BufferedImage srcBufferedImage = ImageIO.read(srcFile);
                int srcWidth = srcBufferedImage.getWidth();
                int srcHeight = srcBufferedImage.getHeight();
                int width = destWidth;
                int height = destHeight;
                if (srcHeight >= srcWidth) {
                    width = (int) Math.round(((destHeight * 1.0 / srcHeight) * srcWidth));
                } else {
                    height = (int) Math.round(((destWidth * 1.0 / srcWidth) * srcHeight));
                }
                BufferedImage destBufferedImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
                graphics2D = destBufferedImage.createGraphics();
                graphics2D.setBackground(BACKGROUND_COLOR);
                graphics2D.clearRect(0, 0, destWidth, destHeight);
                graphics2D.drawImage(srcBufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), (destWidth / 2) - (width / 2), (destHeight / 2) - (height / 2), null);

                imageOutputStream = ImageIO.createImageOutputStream(destFile);
                imageWriter = ImageIO.getImageWritersByFormatName(FilenameUtils.getExtension(destFile.getName())).next();
                imageWriter.setOutput(imageOutputStream);
                ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
                imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                imageWriteParam.setCompressionQuality((float) (DEST_QUALITY / 100.0));
                imageWriter.write(null, new IIOImage(destBufferedImage, null, null), imageWriteParam);
                imageOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (graphics2D != null) {
                    graphics2D.dispose();
                }
                if (imageWriter != null) {
                    imageWriter.dispose();
                }
                if (imageOutputStream != null) {
                    try {
                        imageOutputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
        } else {
            IMOperation operation = new IMOperation();
            operation.thumbnail(destWidth, destHeight);
            operation.gravity("center");
            operation.background(toHexEncoding(BACKGROUND_COLOR));
            operation.extent(destWidth, destHeight);
            operation.quality((double) DEST_QUALITY);
            operation.addImage(srcFile.getPath());
            operation.addImage(destFile.getPath());
            if (type == Type.graphicsMagick) {
                ConvertCmd convertCmd = new ConvertCmd(true);
                if (graphicsMagickPath != null) {
                    convertCmd.setSearchPath(graphicsMagickPath);
                }
                try {
                    convertCmd.run(operation);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IM4JavaException e) {
                    e.printStackTrace();
                }
            } else {
                ConvertCmd convertCmd = new ConvertCmd(false);
                if (imageMagickPath != null) {
                    convertCmd.setSearchPath(imageMagickPath);
                }
                try {
                    convertCmd.run(operation);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IM4JavaException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 添加水印
     *
     * @param srcFile           源文件
     * @param destFile          目标文件
     * @param watermarkFile     水印文件
     * @param watermarkPosition 水印位置
     * @param alpha             水印透明度
     */
    public static void addWatermark(File srcFile, File destFile, File watermarkFile, Setting.WatermarkPosition watermarkPosition, int alpha) {
        Assert.notNull(srcFile);
        Assert.notNull(destFile);
        Assert.state(alpha >= 0);
        Assert.state(alpha <= 100);
        if (watermarkFile == null || !watermarkFile.exists() || watermarkPosition == null || watermarkPosition == Setting.WatermarkPosition.no) {
            try {
                FileUtils.copyFile(srcFile, destFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        if (type == Type.jdk) {
            Graphics2D graphics2D = null;
            ImageOutputStream imageOutputStream = null;
            ImageWriter imageWriter = null;
            try {
                BufferedImage srcBufferedImage = ImageIO.read(srcFile);
                int srcWidth = srcBufferedImage.getWidth();
                int srcHeight = srcBufferedImage.getHeight();
                BufferedImage destBufferedImage = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
                graphics2D = destBufferedImage.createGraphics();
                graphics2D.setBackground(BACKGROUND_COLOR);
                graphics2D.clearRect(0, 0, srcWidth, srcHeight);
                graphics2D.drawImage(srcBufferedImage, 0, 0, null);
                graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha / 100F));

                BufferedImage watermarkBufferedImage = ImageIO.read(watermarkFile);
                int watermarkImageWidth = watermarkBufferedImage.getWidth();
                int watermarkImageHeight = watermarkBufferedImage.getHeight();
                int x = srcWidth - watermarkImageWidth;
                int y = srcHeight - watermarkImageHeight;
                if (watermarkPosition == Setting.WatermarkPosition.topLeft) {
                    x = 0;
                    y = 0;
                } else if (watermarkPosition == Setting.WatermarkPosition.topRight) {
                    x = srcWidth - watermarkImageWidth;
                    y = 0;
                } else if (watermarkPosition == Setting.WatermarkPosition.center) {
                    x = (srcWidth - watermarkImageWidth) / 2;
                    y = (srcHeight - watermarkImageHeight) / 2;
                } else if (watermarkPosition == Setting.WatermarkPosition.bottomLeft) {
                    x = 0;
                    y = srcHeight - watermarkImageHeight;
                } else if (watermarkPosition == Setting.WatermarkPosition.bottomRight) {
                    x = srcWidth - watermarkImageWidth;
                    y = srcHeight - watermarkImageHeight;
                }
                graphics2D.drawImage(watermarkBufferedImage, x, y, watermarkImageWidth, watermarkImageHeight, null);

                imageOutputStream = ImageIO.createImageOutputStream(destFile);
                imageWriter = ImageIO.getImageWritersByFormatName(FilenameUtils.getExtension(destFile.getName())).next();
                imageWriter.setOutput(imageOutputStream);
                ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
                imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                imageWriteParam.setCompressionQuality(DEST_QUALITY / 100F);
                imageWriter.write(null, new IIOImage(destBufferedImage, null, null), imageWriteParam);
                imageOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (graphics2D != null) {
                    graphics2D.dispose();
                }
                if (imageWriter != null) {
                    imageWriter.dispose();
                }
                if (imageOutputStream != null) {
                    try {
                        imageOutputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
        } else {
            String gravity = "SouthEast";
            if (watermarkPosition == Setting.WatermarkPosition.topLeft) {
                gravity = "NorthWest";
            } else if (watermarkPosition == Setting.WatermarkPosition.topRight) {
                gravity = "NorthEast";
            } else if (watermarkPosition == Setting.WatermarkPosition.center) {
                gravity = "Center";
            } else if (watermarkPosition == Setting.WatermarkPosition.bottomLeft) {
                gravity = "SouthWest";
            } else if (watermarkPosition == Setting.WatermarkPosition.bottomRight) {
                gravity = "SouthEast";
            }
            IMOperation operation = new IMOperation();
            operation.gravity(gravity);
            operation.dissolve(alpha);
            operation.quality((double) DEST_QUALITY);
            operation.addImage(watermarkFile.getPath());
            operation.addImage(srcFile.getPath());
            operation.addImage(destFile.getPath());
            if (type == Type.graphicsMagick) {
                CompositeCmd compositeCmd = new CompositeCmd(true);
                if (graphicsMagickPath != null) {
                    compositeCmd.setSearchPath(graphicsMagickPath);
                }
                try {
                    compositeCmd.run(operation);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IM4JavaException e) {
                    e.printStackTrace();
                }
            } else {
                CompositeCmd compositeCmd = new CompositeCmd(false);
                if (imageMagickPath != null) {
                    compositeCmd.setSearchPath(imageMagickPath);
                }
                try {
                    compositeCmd.run(operation);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IM4JavaException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 添加水印
     *
     * @param srcImage       源图片
     * @param watermarkImage 水印图片
     * @param x              图片x坐标
     * @param y              图片y坐标
     * @param alpha          水印透明度
     */
    public static BufferedImage addImage(BufferedImage srcImage, BufferedImage watermarkImage, int x, int y, int alpha) {
        Assert.notNull(srcImage);
        Assert.state(alpha >= 0);
        Assert.state(alpha <= 100);
        if (watermarkImage == null) {
            return srcImage;
        }
        Graphics2D graphics2D = null;
        BufferedImage destImage = null;
        try {
            int srcWidth = srcImage.getWidth();
            int srcHeight = srcImage.getHeight();
            destImage = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
            graphics2D = destImage.createGraphics();
            graphics2D.setBackground(BACKGROUND_COLOR);
            graphics2D.clearRect(0, 0, srcWidth, srcHeight);
            graphics2D.drawImage(srcImage, 0, 0, null);
            graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha / 100F));

            int watermarkImageWidth = watermarkImage.getWidth();
            int watermarkImageHeight = watermarkImage.getHeight();

            graphics2D.drawImage(watermarkImage, x, y, watermarkImageWidth, watermarkImageHeight, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (graphics2D != null) {
                graphics2D.dispose();
            }
        }

        return destImage;
    }

    
    /**
     * add text
     * @param srcImage
     * @param content
     * @param fontSize
     * @param x
     * @param y
     * @param alpha
     * @return
     */
    public static BufferedImage addText(BufferedImage srcImage, String content, Color fontColor, int fontSize, int x, int y, int alpha){
        Assert.notNull(srcImage);
        Assert.state(alpha >= 0);
        Assert.state(alpha <= 100);
        if (content == null || content.equals("")) {
            return srcImage;
        }
        Graphics2D graphics2D = null;
        BufferedImage destImage = null;
        try {
            int srcWidth = srcImage.getWidth();
            int srcHeight = srcImage.getHeight();
            destImage = new BufferedImage(srcWidth, srcHeight, BufferedImage.TYPE_INT_RGB);
            graphics2D = destImage.createGraphics();
            graphics2D.setColor(fontColor);
            graphics2D.setBackground(BACKGROUND_COLOR);
            graphics2D.clearRect(0, 0, srcWidth, srcHeight);
            graphics2D.drawImage(srcImage, 0, 0, null);
            graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha / 100F));
            AttributedString attributedString = new AttributedString(content);
            Font font = new Font("宋体", Font.BOLD, fontSize);
            attributedString.addAttribute(TextAttribute.FONT, font, 0, content.length());
            AttributedCharacterIterator iterator = attributedString.getIterator();
            graphics2D.drawString(iterator, x, y);
            ImageIO.write(destImage, "jpg", new File("/hboxs/image.jpg"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (graphics2D != null) {
                graphics2D.dispose();
            }
        }

        return destImage;
    }
    /**
     * 初始化
     */
    public static void initialize() {
    }

    /**
     * 转换颜色为十六进制代码
     *
     * @param color 颜色
     * @return 十六进制代码
     */
    private static String toHexEncoding(Color color) {
        String R, G, B;
        StringBuffer stringBuffer = new StringBuffer();
        R = Integer.toHexString(color.getRed());
        G = Integer.toHexString(color.getGreen());
        B = Integer.toHexString(color.getBlue());
        R = R.length() == 1 ? "0" + R : R;
        G = G.length() == 1 ? "0" + G : G;
        B = B.length() == 1 ? "0" + B : B;
        stringBuffer.append("#");
        stringBuffer.append(R);
        stringBuffer.append(G);
        stringBuffer.append(B);
        return stringBuffer.toString();
    }

    /**
     * Dinfeng 图片裁剪
     *
     * @param src
     * @param
     * @param x
     * @param y
     * @param w
     * @param h
     * @throws IOException
     */
    public static String cutImage(MultipartFile src, int x, int y, int w, int h) {

        Setting setting = SettingUtils.get();
        // 头像tmp文件夹
        String avatorTmpUploadPath = setting.getAvatorTmpUploadPath();
        // 头像文件夹
        String avatorUploadPath = setting.getAvatorUploadPath();

        String ext = src.getOriginalFilename().substring(src.getOriginalFilename().lastIndexOf(".") + 1);
        if(ext==""||ext==null) ext="jpg";
        ImageInputStream iis = null;
        InputStream is = null;

        try {
            is = src.getInputStream();

            // 获取图片流
            iis = ImageIO.createImageInputStream(is);

            /**
             *
             * 返回包含所有当前已注册 ImageReader 的 Iterator，这些 ImageReader
             *
             * 声称能够解码指定格式。 参数：formatName - 包含非正式格式名称 .
             *
             * (例如 "jpeg" 或 "tiff")等 。
             */

            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(ext);
            ImageReader reader = it.next();

            /**
             *
             * <p>
             * iis:读取源。true:只向前搜索
             * </p>
             * .将它标记为 ‘只向前搜索’。
             *
             * 此设置意味着包含在输入源中的图像将只按顺序读取，可能允许 reader
             *
             * 避免缓存包含与以前已经读取的图像关联的数据的那些输入部分。
             */
            reader.setInput(iis, true);
            ImageReadParam param = reader.getDefaultReadParam();
            // 根据坐标点长宽截取图片
            Rectangle rect = new Rectangle(x, y, w, h);
            param.setSourceRegion(rect);

            /**
             *
             * 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象，并将
             *
             * 它作为一个完整的 BufferedImage 返回。
             */
            BufferedImage bi = reader.read(0, param);

            Map<String, Object> model = new HashMap<String, Object>();

            String tmpPath = FreemarkerUtils.process(avatorTmpUploadPath, model);
            String path = FreemarkerUtils.process(avatorUploadPath, model);
            // 文件名
            String imageName = UUID.randomUUID().toString() + "." + ext;

            File tmpFolder = new File(setting.getRootPath() + tmpPath);
            // 如果文件夹不存在则创建
            if (!tmpFolder.exists() && !tmpFolder.isDirectory()) {
                tmpFolder.mkdir();
            }

            ImageIO.write(bi, ext, new File(setting.getRootPath() + tmpPath + imageName));
            zoom(new File(setting.getRootPath() + tmpPath + imageName), new File(setting.getRootPath() + path + imageName), IMAGEWIDTH, IMAGEWIDTH);
            return imageName;
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        } finally {
            try {
                iis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;

    }
}